// apply/call/bind
// curry/compose
// debounce/throttle
// 寄生组合继承

Function.prototype.myCall = function (context, ...args) {
    let key = Symbol();
    context[key] = this;
    let res = context[key](...args);
    delete context[key];
    return res;
}

Function.prototype.myApply = function (context, ...args) {
    let key = Symbol();
    context[key] = this;
    let res;
    if (Array.isArray(args)) {
        res = context[key](args);
    } else {
        res = context[key]();
    }

    delete context[key];
    return res;
}

Function.prototype.myBind = function (context, ...args) {
    return (...args) => {
        let key = Symbol();
        context[key] = this;
        let res = context[key](...args);
        delete context[key];
        return res;
    }
}

// 有限
function curry(fn){
    return function curryFn(...args){
        if(args.length >= fn.length){
            return fn.call(this, ...args)
        } else {
            return function(...resetArgs){
                return curryFn.apply(this, [...args, ...resetArgs])
            }
        }
    }
}
var a = (a, b, c, d, e) => a + b + c + d + e
let newFn = curry(a)
console.log(newFn(1, 2)(3, 4, 5, 6, 6))



// 无限 sum(1)(2)(3)
function curry2(...args){
    let arr = args
    function sum(...params){
        arr = [...args, ...params]
        return sum
    }
    sum.toString = () => {
        return arr.reduce((pre, next) => (pre + next), 0)
    }
    return sum
}
console.log(curry2(1)(2)(3).toString())



function compose(...args){
    return function(val){
        return args.reduceRight((prev, next) => next(prev), val)
    }
}
let a1 = (a) => a + a;
let a2 = (a) => a * a;
console.log(compose(a1, a2)(10))


function debounce(fn, time){
    let timer = null;
    return (...args) => {
        clearTimeout(timer)
        timer = setTimeout(() => {
            fn.call(this, ...args)
        }, time)
    }
}

function betterDebounce(fn, time, immediate){
    let timer = null;
    return (...args) => {
        if(timer) clearTimeout(timer)
        if(immediate && !timer){
            timer = setTimeout(() => {
                timer = null
            }, time)
            fn.call(this, ...args)
        } else {
            timer = setTimeout(() => {
                fn.call(this, ...args);
                timer = null;
            }, time);
        }
    }
}

function throttle(fn, wait){
    let timer = null;
    return (...args) => {
        if(!timer){
            timer = setTimeout(() => {
                fn.call(this, ...args)
                timer = null
            }, wait)
        }
    }
    
}

function betterThrottle(fn, wait){
    let timer = null;
    let start = Date.now();
    return (...args) => {
        let currtime = Date.now();
        let time = wait - (currtime - start);
        if(time < 0){
            if(timer){
                clearTimeout(timer)
                timer = null
            }
            fn.call(this, ...args);
            start = Date.now()
        } else {
            if(!timer){
                timer = setTimeout(() => {
                    fn.call(this, ...args)
                    timer = null
                    start = Date.now()
                }, wait)
            }
        }
    }
}



function fa(){}
function inherit(son, father){
    let newSon = Object.create(father.prototype);
    son.prototype = newSon;
    son.prototype.constructor = son
}

function son(...args){
    fa.call(this, ...args);
    inherit(son, fa)
}